//
//  ExploitManager.m
//  Dopamine
//
//  Created by Lars Fröder on 09.01.24.
//

#import "DOExploitManager.h"
#import "DOExploit.h"
#import "DOEnvironmentManager.h"
#import "DOPreferenceManager.h"

#define FORCE_PAC_BYPASS 0

@implementation DOExploitManager

+ (instancetype)sharedManager
{
    static DOExploitManager *shared;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        shared = [[DOExploitManager alloc] init];
    });
    return shared;
}

- (instancetype)init
{
    self = [super init];
    if (self) {
        _activeExploits = [NSMutableArray new];
        [self _loadAvailableExploits];
    }
    return self;
}

- (DOExploit *)_findPreferredExploitForType:(ExploitType)type
{
    DOExploit *preferredExploit = nil;
    for (DOExploit *exploit in [self availableExploitsForType:type]) {
        if (!preferredExploit || exploit.priority > preferredExploit.priority) {
            preferredExploit = exploit;
        }
    }
    return preferredExploit;
}

- (DOExploit *)_findExploitWithIdentifier:(NSString *)identifier andType:(ExploitType)type
{
    for (DOExploit *exploit in [self availableExploitsForType:type]) {
        if ([exploit.identifier isEqualToString:identifier]) return exploit;
    }
    return nil;
}

- (void)_loadAvailableExploits
{
    NSMutableSet *exploitSet = [NSMutableSet new];
    NSString *frameworksPath = [[NSBundle mainBundle].bundlePath stringByAppendingPathComponent:@"Frameworks"];
    NSArray *contents = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:frameworksPath error:nil];
    if (contents) {
        for (NSString *frameworkName in contents) {
            NSString *frameworkPath = [frameworksPath stringByAppendingPathComponent:frameworkName];
            NSDictionary *frameworkInfo = [NSDictionary dictionaryWithContentsOfFile:[frameworkPath stringByAppendingPathComponent:@"Info.plist"]];
            if (frameworkInfo) {
                if (frameworkInfo[@"DPExploitType"]) {
                    NSDictionary *flavors = frameworkInfo[@"DPExploitFlavors"];
                    [flavors enumerateKeysAndObjectsUsingBlock:^(NSString *flavorName, NSDictionary *flavorDict, BOOL *stop) {
                        [exploitSet addObject:[[DOExploit alloc] initWithPath:frameworkPath info:frameworkInfo flavorName:flavorName flavorDictionary:flavorDict]];
                    }];
                }
            }
        }
    }
    _allExploits = [exploitSet copy];
    
    for (ExploitType i = 0; i < EXPLOIT_TYPE_COUNT; i++) {
        _preferredExploitForType[i] = [self _findPreferredExploitForType:i];
    }
    
}

- (NSSet<DOExploit *> *)availableExploitsForType:(ExploitType)type
{
    NSMutableSet *subset = [NSMutableSet new];
    for (DOExploit *exploit in _allExploits) {
        if (exploit.type == type) {
            if ([exploit isSupported]) {
                [subset addObject:exploit];
            }
        }
    }
    return subset;
}

- (DOExploit *)preferredKernelExploit
{
    return _preferredExploitForType[EXPLOIT_TYPE_KERNEL];
}

- (DOExploit *)preferredPACBypass
{
    if (![DOEnvironmentManager sharedManager].isPACBypassRequired) return nil;
    return _preferredExploitForType[EXPLOIT_TYPE_PAC];
}

- (DOExploit *)preferredPPLBypass
{
    return _preferredExploitForType[EXPLOIT_TYPE_PPL];
}

- (DOExploit *)selectedKernelExploit
{
    NSString *identifier = [[DOPreferenceManager sharedManager] preferenceValueForKey:@"selectedKernelExploit"];
    if (!identifier) {
        return [self preferredKernelExploit];
    }
    return [self _findExploitWithIdentifier:identifier andType:EXPLOIT_TYPE_KERNEL];
}

- (DOExploit *)selectedPACBypass
{
    NSString *identifier = [[DOPreferenceManager sharedManager] preferenceValueForKey:@"selectedPACBypass"];
    if (!identifier) {
        return [self preferredPACBypass];
    }
    return [self _findExploitWithIdentifier:identifier andType:EXPLOIT_TYPE_PAC];
}

- (DOExploit *)selectedPPLBypass
{
    NSString *identifier = [[DOPreferenceManager sharedManager] preferenceValueForKey:@"selectedPPLBypass"];
    if (!identifier) {
        return [self preferredPPLBypass];
    }
    return [self _findExploitWithIdentifier:identifier andType:EXPLOIT_TYPE_PPL];
}

- (int)cleanUpExploits
{
    for (DOExploit *exploit in [_activeExploits reverseObjectEnumerator]) {
        int r = [exploit cleanup];
        if (r != 0) return r;
    }
    return 0;
}

@end
